#include "PlaylistServer.h"

using namespace std;

/*---------------------------------------------------------------------------------------
--	FUNCTION:	PlaylistServer::PlaylistServer
--
--	DATE:			March 22, 2010
--
--	REVISIONS		(Date and Description):
--
--	DESIGNER:		Brad Paugh
--
--	PROGRAMMER:		Brad Paugh
--
--      INTERFACE:      PlaylistServer::PlaylistServer()
--
--	RETURNS:		
--
--	NOTES:
--		Constructor for the Playlist server, which starts up windows WSA.
---------------------------------------------------------------------------------------*/
PlaylistServer::PlaylistServer()
{
	WORD wVersionRequested = MAKEWORD(2,2);
	WSADATA WSAdata;
	WSAStartup(wVersionRequested, &WSAdata);
}

/*---------------------------------------------------------------------------------------
--	FUNCTION:	PlaylistServer::runServer
--
--	DATE:			March 22, 2010
--
--	REVISIONS		(Date and Description):
--
--	DESIGNER:		Brad Paugh
--
--	PROGRAMMER:		Brad Paugh
--
--  INTERFACE:      void PlaylistServer::runServer()
--
--	RETURNS:		void
--
--	NOTES:
--		this function runs the TCP playlist Server, uses the windows overlapped structure
--		and creates a new thread to service each connected client.
---------------------------------------------------------------------------------------*/
void PlaylistServer::runServer()
{
    startRunning();
}


void PlaylistServer::run()
{
    Socket* s = new Socket();
    s->Create("TCP");
    s->Bind(PORT);
    s->Listen();

    while(1)
    {
            Socket* newSocket = new Socket(s->Accept());
            connections_.push_back(*newSocket);
            CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadStart, this, 0, NULL);
            sendPlaylistToClient(*newSocket);
    }
}

/*---------------------------------------------------------------------------------------
--	FUNCTION:	PlaylistServer::receiveThread
--
--	DATE:			March 22, 2010
--
--	REVISIONS		(Date and Description):
--
--	DESIGNER:		Brad Paugh
--
--	PROGRAMMER:		Brad Paugh
--
--  INTERFACE:      void PlaylistServer::receiveThread(Socket clientSocket)
--						Socket clientSocket - the socket that this thread services
--
--	RETURNS:		void
--
--	NOTES:
--		This is the thread implementation for the receive thread. It does the processing
--		for everything sent to the server from the client (votes and file uploads). We need
--		a way of knowing if the received request was intended to be an upload or a vote, so
--		we use the special characters DC1 and DC2 to determine this.
--
--		Packet Structure
--		vote: \x11FILENAME
--		upload: \x12FILENAME
---------------------------------------------------------------------------------------*/
void PlaylistServer::receiveThread(Socket clientSocket)
{
	string buffer;
	string filename;
	OVERLAPPED overlapped;
	HANDLE hThread;
	int bytesReceived;

        ZeroMemory(&overlapped, sizeof(overlapped));

	cout << "Connection Established" << endl;

	if ((overlapped.hEvent = WSACreateEvent()) == WSA_INVALID_EVENT)
	{
		cout << "event error: " << WSAGetLastError();
		exit(1);
	}

	while (1)
	{
		bytesReceived = clientSocket.Recv(buffer, MESSAGE_SIZE, &overlapped);
		filename = buffer.substr(1);
		//client disconnect
		if (bytesReceived == 0)
		{
			removeSocket(clientSocket);
			return;
		}
		//if vote
		if (buffer.at(0) == '\x11')
		{
			handleVote(filename);
		}
		//if upload file
		if (buffer.at(0) == '\x12')
		{
			receiveFile(filename, clientSocket, &overlapped);
		}
	}
}

/*---------------------------------------------------------------------------------------
--	FUNCTION:	PlaylistServer::threadStart
--
--	DATE:			March 22, 2010
--
--	REVISIONS		(Date and Description):
--
--	DESIGNER:		Brad Paugh
--
--	PROGRAMMER:		Brad Paugh
--
--  INTERFACE:      void PlaylistServer::threadStart(void* param)
--						void* param - thread parameter
--
--	RETURNS:		void
--
--	NOTES:
--		A member function cannot be passed into CreateThread so we need to use this static
--		function to start the thread.
---------------------------------------------------------------------------------------*/
void PlaylistServer::threadStart(void* param)
{
	( (PlaylistServer*)param)->receiveThread( ((PlaylistServer*)param)->connections_.back() );
}

/*---------------------------------------------------------------------------------------
--	FUNCTION:	PlaylistServer::removeSocket
--
--	DATE:			March 22, 2010
--
--	REVISIONS		(Date and Description):
--
--	DESIGNER:		Brad Paugh
--
--	PROGRAMMER:		Brad Paugh
--
--  INTERFACE:      void PlaylistServer::removeSocket(Socket s)
--						Socket s - The socket to be removed
--
--	RETURNS:		void
--
--	NOTES:
--		When a client disconnects their socket must be removed from the vector of "connections"
--		as they are no longer connected to the server. This method removes the passed in socket
--		from the vector.
---------------------------------------------------------------------------------------*/
void PlaylistServer::removeSocket(Socket s)
{
	vector<Socket>::iterator it;
	for (it = connections_.begin(); it != connections_.end(); it++)
	{
		if ((*it) == s)
		{
			connections_.erase(it);
			break;
		}
	}
}

/*---------------------------------------------------------------------------------------
--	FUNCTION:	PlaylistServer::sendPlaylistToAll
--
--	DATE:			March 22, 2010
--
--	REVISIONS		(Date and Description):
--
--	DESIGNER:		Brad Paugh
--
--	PROGRAMMER:		Brad Paugh
--
--  INTERFACE:      void PlaylistServer::sendPlaylistToAll()
--
--	RETURNS:		void
--
--	NOTES:
--		Sends the current playlist to all connected clients
---------------------------------------------------------------------------------------*/
void PlaylistServer::sendPlaylistToAll()
{
	string playlist = playlist_.parseToString();
	vector<Socket>::iterator it;
	for (it = connections_.begin(); it != connections_.end(); it++)
	{
		it->Send(playlist, playlist.length());
	}
}

/*---------------------------------------------------------------------------------------
--	FUNCTION:	PlaylistServer::sendPlaylistToClient
--
--	DATE:			March 22, 2010
--
--	REVISIONS		(Date and Description):
--
--	DESIGNER:		Brad Paugh
--
--	PROGRAMMER:		Brad Paugh
--
--  INTERFACE:      void PlaylistServer::sendPlaylistToClient(Socket clientSock)
--						Socket clientSock - the socket to send to
--
--	RETURNS:		void
--
--	NOTES:
--		Sends the current playlist to a single client
---------------------------------------------------------------------------------------*/
void PlaylistServer::sendPlaylistToClient(Socket clientSock)
{
	string playlist = playlist_.parseToString();
	clientSock.Send(playlist, playlist.length());
}

/*---------------------------------------------------------------------------------------
--	FUNCTION:	PlaylistServer::handleVote
--
--	DATE:			March 22, 2010
--
--	REVISIONS		(Date and Description):
--
--	DESIGNER:		Brad Paugh
--
--	PROGRAMMER:		Brad Paugh
--
--  INTERFACE:      void PlaylistServer::handleVote(string filename)
--						string filename - filename of the file that is being voted for
--
--	RETURNS:		void
--
--	NOTES:
--		This function handles what happens when a client votes for a song in the playlist.
---------------------------------------------------------------------------------------*/
void PlaylistServer::handleVote(string filename)
{
	if (playlist_.voteSong(filename) == -1)
	{
		//send notification to client that song was not found
	}
	else
	{
		sendPlaylistToAll();
	}
}

/*---------------------------------------------------------------------------------------
--	FUNCTION:	PlaylistServer::receiveFile
--
--	DATE:			March 22, 2010
--
--	REVISIONS		(Date and Description):
--
--	DESIGNER:		Brad Paugh
--
--	PROGRAMMER:		Brad Paugh
--
--  INTERFACE:      void PlaylistServer::receiveFile(string filename, Socket clientSocket, OVERLAPPED* overlapped)
--						string filename - filename of the file being uploaded
--						Socket clientSocket - socket to receive file from
--						OVERLAPPED overlapped - windows overlapped structure for async IO
--
--	RETURNS:		void
--
--	NOTES:
--		Receives a file from the client, stores it on the server and adds the song to the playlist.
--		when file is finished sending the client will send a packet full of null characters (\0) to
--		indicate the file has finished sending.
---------------------------------------------------------------------------------------*/
void PlaylistServer::receiveFile(string filename, Socket clientSocket, OVERLAPPED* overlapped)
{
	//Cstring buffer to receive binary data
	char* cBuffer;
	cBuffer = (char*)malloc(DATA_SIZE);

	//terminating packet is a packet of all null characters
	char endPacket[DATA_SIZE];
	memset(endPacket, '\0', DATA_SIZE);

	int bytesReceived;
	
	//the location to store file currently hardcoded to /music directory
	char* location;
	location = (char*)malloc(MESSAGE_SIZE + 6);
	strcpy(location, "music/");
	location = strcat(location, filename.c_str());

	ofstream of;
	of.open(location, ios_base::out | ios_base::binary);
	
	while (1)
	{
		bytesReceived = clientSocket.Recv(cBuffer, DATA_SIZE, overlapped);
		
		//client disconnect
		if (bytesReceived == 0)
		{
			removeSocket(clientSocket);
			playlist_.removeSong(filename);
			return;
		}
		//received terminating packet
		if (memcmp(cBuffer, endPacket, sizeof(cBuffer)) == 0)
		{
			break;
		}
		of.write(cBuffer, bytesReceived);
	}
	of.close();
	Song s(filename);
	playlist_.addSong(s);
	sendPlaylistToAll();
}
